using System;
using Server;
using Server.Spells;
using Server.Mobiles;
using Server.Targets;
using Server.Engines.CastSystem;

namespace Server.Items  
{
	public class ArmaBase : BaseWeapon
	{
        // Queste funzioni vanno necessariamente ridefinite (override)
        // Tramite l'overriding di queste funzioni si definiscono i parametri di un'arma
        public virtual String DefNome { get { return "NomeDaDefinire"; } }
        public virtual double DefPeso { get { return -1.0; } }
        public virtual int DefDannoMinimo { get { return -1; } }
        public virtual int DefDannoMassimo { get { return -1; } }
        public virtual SkillName DefSkillUtilizzata { get { return SkillName.Swords; } }
        public virtual WeaponAnimation DefAnimazione { get { return WeaponAnimation.Slash1H; } }
        public virtual WeaponType DefTipo { get { return WeaponType.Taglio; } }
        public virtual int DefSuonoTargetColpito { get { return 0x237; } }
        public virtual int DefSuonoTargetMancato { get { return 0x23A; } }
        public virtual int DefLivelloMinimo { get { return -1; } }
        public virtual Layer DefLayer { get { return Layer.OneHanded; } }

        // Queste sono settate a dei valori di default: possono essere ridefinite se necessario
        public virtual int DefMinPuntiStruttura { get { return 70; } }
        public virtual int DefMaxPuntiStruttura { get { return 110; } }
        public virtual int DefMaxRangeCaricareColpo { get { return 1; } }
        public virtual int DefMaxRangeScaricareColpo { get { return 2; } }
        public virtual int BonusStr { get { return 0; } }
        public virtual int BonusDex { get { return 0; } }
        public virtual int BonusInt { get { return 0; } }
        public virtual int BonusForza { get { return 0; } }

		public ArmaBase( int itemID) : base( itemID )
		{
            Weight = DefPeso;
		}

        public ArmaBase(Serial serial) : base(serial)
		{
		}

		public override void Serialize( GenericWriter writer )
		{
			base.Serialize( writer );
		}

		public override void Deserialize( GenericReader reader )
		{
			base.Deserialize( reader );
		}

        [CommandProperty(AccessLevel.GameMaster)]
        public override double Weight
        {
            get
            {
                double modificatore = 0.0;

                switch (Resource)
                {
                    case CraftResource.Acciaio:
                        {
                            modificatore = -0.5;
                            break;
                        }
                    case CraftResource.Adamantio:
                        {
                            modificatore = -1.0;
                            break;
                        }
                    case CraftResource.Argento:
                        {
                            modificatore = -2.0;
                            break;
                        }

                    // TODO: perso altri materiali  
                }

                return base.Weight + modificatore;
            }

            set
            {
                base.Weight = value;
            }
        }

		//-------Max-------- Target ondoubleclick
		public override void OnDoubleClick( Mobile from )
		{    
            if (from.FindItemOnLayer(this.Layer) != this)
            {
                if (!from.EquipItem(this))
                    return;

                from.PerdiIlColpo();
            }

            if (Tipo == WeaponType.Punta || Tipo == WeaponType.Taglio)
            {
                from.SendLocalizedMessage(1010018); // What do you want to use this item on?
                from.Target = new BladedItemTarget(this);
            }

            // TODO: che facciamo quando clicchiamo su un'arma contundenti?
		}
		//-------Max------- 

        // Calcolo probabilita' di colpire l'avversario
        public override bool CheckHit(Mobile attacker, Mobile defender, double bonusOrMalus)
        {
            BaseWeapon atkWeapon = attacker.Weapon as BaseWeapon;
            BaseWeapon defWeapon = defender.Weapon as BaseWeapon;

            Skill atkSkill = attacker.Skills[atkWeapon.Skill];
            Skill defSkill = defender.Skills[defWeapon.Skill];

            double atkValue = atkWeapon.GetAttackSkillValue(attacker, defender);
            double defValue = defWeapon.GetDefendSkillValue(attacker, defender);

            // TODO: farlo meglio, ora funziona solo se il pg e' voltato nella stessa direzione.
            // TODO: furfanti, ladri e assassini prendono un +18 sempre. Le altre classi beccano il +18 se hiddati
            if (attacker.Direction == defender.Direction)
                atkValue = atkValue + 12.0;

            // Formula descritta sul sito
            double skillComponent = ((atkValue - defValue) * 2.5) / 3.0;

            double chance = (50.0 + skillComponent + bonusOrMalus) / 100.0;

            attacker.SendMessage("Percentuale di colpire: {0}%", Math.Round(chance * 100));

            return (chance > Utility.RandomDouble());
        }

        public override void OnHit(Mobile attacker, Mobile defender, double damageBonus)
        {
            // Schivi il colpo?
            if (defender.Schivare > Utility.RandomDouble() * 100)
            {
                defender.SendMessage("Schivi il colpo!"); 
                attacker.SendMessage(defender.Name + " ha schivato il colpo!"); 
                OnMiss(attacker, defender);
                return;
            }

            int damage = ComputeDamage(attacker, defender);

            damage = AbsorbDamage(attacker, defender, damage);

            if (damage > 0)
                AddBlood(attacker, defender, damage);

            //PlayHurtAnimation(defender);
            defender.Damage(damage, attacker);
        }

        public override void OnMiss(Mobile attacker, Mobile defender)
        {
            attacker.PlaySound(GetMissAttackSound(attacker, defender));
            defender.PlaySound(GetMissDefendSound(attacker, defender));
        }

        // Calcolo danno arma
        public override int ComputeDamage(Mobile attacker, Mobile defender)
        {
            // weapon damage
            int damage = Utility.RandomMinMax(DannoMin, DannoMax);

            int modificatore = 0;

            switch (Resource)
            {
                case CraftResource.Acciaio:
                    {
                        modificatore = 1;
                        break;
                    }
                case CraftResource.Adamantio:
                    {
                        modificatore = 2;
                        break;
                    }
                case CraftResource.Argento:
                    {
                        modificatore = -1;
                        break;
                    }
                // TODO: perso altri materiali        
            }

            // Modificatore materiale
            damage = damage + modificatore;

            // Incantamento
            damage = damage + this.Incantamento;

            if (Tipo == WeaponType.Ranged)
            {
                // 1 danno per ogni 10 punti di dex sopra il 35
                damage = damage + (int)Math.Floor((attacker.Dex - 35) / 10.0);
            } else
            {
                // Forza - 10
                if (attacker.Forza > 10)
                    damage = damage + attacker.Forza - 10;
            }

            // Tattica / 8
            damage = damage + ((int)attacker.Skills[SkillName.Tactics].Value) / 8;

            // Anatomia / 24
            damage = damage + ((int)attacker.Skills[SkillName.Anatomy].Value) / 24;

            return damage;
        }

        // Assorbimento danni
        public override int AbsorbDamage(Mobile attacker, Mobile defender, int damage)
        {
            bool blocked = false;

            //attacker.SendMessage("DEBUG: Infliggi {0} danni", damage);

            // DIFESE GENERALI
            damage = damage - defender.Difesa / 2;

            // SCUDO
            if (defender.Player || defender.Body.IsHuman)
            {
                blocked = CheckColpoParato(attacker, defender);

                if (blocked)
                {
                    // Parata ad impatto
                    if (CastSystem.CurseOrBlessLookUp(defender, typeof(ParataAdImpatto)))
                    {
                        // 10 danni riflessi
                        attacker.Damage(10, defender);
                        attacker.FixedEffect(0x37B9, 10, 16, 0x279, 0);
                    }

                    ArmaturaBase shield = defender.FindItemOnLayer(Layer.TwoHanded) as ArmaturaBase;

                    if (shield != null)
                    {
                        double parry = defender.Skills[SkillName.Parry].Value;

                        shield.OnHit(this, damage);

                        int parredDamage = shield.ArmorBase / 2 + shield.MagicBonus + ((int)Math.Round(parry) / Utility.RandomMinMax(10, 22));

                        // Lo scudo in caso di parata riduce il danno di (difesa/2 + incantamento magico + questa cosa mezza random qui sopra basata sulla skill parare).
                        damage = damage - parredDamage;

                        defender.aggiornaDirezioneDuranteCaricaScarica(attacker, false);

                        //attacker.SendMessage("DEBUG: {0} danni parati", parredDamage);
                        attacker.SendMessage(defender.Name + " para il colpo!");
                        defender.SendMessage("Pari il colpo!");
                    }
                }
            }

            if (blocked)
                attacker.PlaySound(GetHitBlockSound(attacker, defender));
            else
                attacker.PlaySound(GetHitAttackSound(attacker, defender));

            defender.PlaySound(GetHitDefendSound(attacker, defender));

            // ARMATURA
            double positionChance = Utility.RandomDouble();

            Item armorItem;


            if (positionChance < 0.05)
            {
                // Collo 5%
                armorItem = defender.NeckArmor;
                attacker.SendMessage("Colpisci al collo");
                defender.SendMessage("Vieni colpito al collo");
            }
            else if (positionChance < 0.10)
            {
                // Mani 5%
                armorItem = defender.HandArmor;
                attacker.SendMessage("Colpisci alle mani");
                defender.SendMessage("Vieni colpito alle mani");
            }
            else if (positionChance < 0.30)
            {
                // Braccia 20%
                armorItem = defender.ArmsArmor;
                attacker.SendMessage("Colpisci alle braccia");
                defender.SendMessage("Vieni colpito alle braccia");
            }
            else if (positionChance < 0.40)
            {
                // Testa 10%
                armorItem = defender.HeadArmor;
                attacker.SendMessage("Colpisci alla testa");
                defender.SendMessage("Vieni colpito alla testa");
            }
            else if (positionChance < 0.55)
            {
                // Gambe 15%
                armorItem = defender.LegsArmor;
                attacker.SendMessage("Colpisci alle gambe");
                defender.SendMessage("Vieni colpito alle gambe");
            }
            else if (positionChance < 0.60)
            {
                // Piedi 5%
                armorItem = defender.FeetArmor;
                attacker.SendMessage("Colpisci ai piedi");
                defender.SendMessage("Vieni colpito ai piedi");
            }
            else if (positionChance < 0.65)
            {
                // Inguine 5%
                armorItem = defender.InguineArmor;
                attacker.SendMessage("Colpisci all'inguine");
                defender.SendMessage("Vieni colpito all'inguine");
            }
            else
            {
                // Corpo 35%
                armorItem = defender.ChestArmor;
                attacker.SendMessage("Colpisci al corpo");
                defender.SendMessage("Vieni colpito al corpo");
            }



            IWearableDurability armor = armorItem as IWearableDurability;

            if (armor != null)
            {
                if (armor is BaseArmor)
                {
                    // L'armatura para (difesa/2 + incantamento) danni
                    int riduzioneDanno = (armor as BaseArmor).BaseArmorRating / 2 + (armor as BaseArmor).MagicBonus;
                    //attacker.SendMessage("DEBUG: {0} assorbiti dall'armatura", riduzioneDanno);
                    damage = damage - riduzioneDanno;
                }

                armor.OnHit(this, damage); // call OnHit to lose durability                
            }

            if (damage < 0)
                damage = 0;

            //attacker.SendMessage("DEBUG: {0} danni vanno a segno", damage);

            return damage;
        }

        public static bool CheckColpoParato(Mobile attacker, Mobile defender)
        {
            if (defender == null)
                return false;

            ArmaturaBase shield = defender.FindItemOnLayer(Layer.TwoHanded) as ArmaturaBase;

            double parry = defender.Skills[SkillName.Parry].Value;

            if (shield != null)
            {
                double chance;

                if (attacker == null)
                {
                    chance = parry / 100.0;
                }
                // La % di parata e' pari alla skill parare - livello*2 dell'avversario.
                else
                {
                    chance = (parry - (attacker.Livello) * 2) / 100.0;
                }

                if (Utility.RandomDouble() < chance)
                    return true;
            }
            else if (!(defender.Weapon is Fists) && !(defender.Weapon is BaseRanged))
            {
                // Parare anche con l'arma da mischia?
            }

            return false;
        }

        // Questa funzione viene chiamata quando viene caricato il colpo e ritorna il tempo
        // di carica del colpo. 
        public override double StartSwingMovement(Mobile attacker, Mobile defender)
        {
            bool canSwing = true;
            
            canSwing = (!attacker.Paralyzed && !attacker.Frozen);

            if (canSwing)
            {
                Spell sp = attacker.Spell as Spell;

                canSwing = (sp == null || !sp.IsCasting || !sp.BlocksMovement);
            }

            if (canSwing)
            {
                PlayerMobile p = attacker as PlayerMobile;

                canSwing = (p == null || p.PeacedUntil <= DateTime.Now);
            }
           

            if (canSwing)
            {
                // Aggiusta la direzione dell'attaccante a meno che i due PG sono sullo stesso tile.
                if (!attacker.InRange(defender, 0))
                    attacker.aggiornaDirezioneDuranteCaricaScarica(defender, true);

                double animDelay = GetSwingAnimationDelay(attacker);

                // Leggiadria: setta il delay di carica colpo a 0.1
                if (attacker.LeggiadriaAttiva)
                    animDelay = 0.1;

                if ((attacker.Direction & Direction.Running) == 0
                    || DateTime.Now > attacker.LastMoveTime + TimeSpan.FromSeconds(0.1))
                {
                    //attacker.SendMessage("DEBUG: tempo carica colpo {0}", animDelay);
                    PlaySwingAnimation(attacker, animDelay);
                }
                return animDelay;
            }
            else
            {
                return -1.0;
            }
        }

        public override TimeSpan OnSwing(Mobile attacker, Mobile defender, double damageBonus)
        {
            if (attacker.HarmfulCheck(defender))
            {
                // Aggiusta la direzione dell'attaccante a meno che i due PG sono sullo stesso tile.
                if (!attacker.InRange(defender, 0))
                    attacker.aggiornaDirezioneDuranteCaricaScarica(defender, false);

                attacker.DisruptiveAction();

                if (CheckHit(attacker, defender))
                    OnHit(attacker, defender, damageBonus);
                else
                    OnMiss(attacker, defender);
            }

            return GetDelay(attacker);
        }

        // Questa funzione ritorna la durata del periodo di caricamento del colpo.
        public override double GetSwingAnimationDelay(Mobile attacker)
        {
            // Assumiamo che destrezza sia influente nello swing nei valori 0-90.
            // Diciamo che a 90 si swinga a piena velocita (quello dato dal peso dell'arma).
            // Tra 0 e 89 si ottiene un malus moltiplicatore del delay. Con destrezza 0 il moltiplicatore e' 3.
            double malusMultiplicator = 1.0;
            if (attacker.Dex < 90)
                malusMultiplicator = malusMultiplicator + ((90.0 - (double)attacker.Dex) / 90.0) * 2;

            attacker.SendMessage("DEBUG: peso arma {0}", Weight);
            // Le altre armi hanno 0.33 secondi di delay per ogni stone. Un'arma peso 6 (falcetto) ha delay 2
            // Un'arma lenta tipo l'alabarda (peso 14) ha delay 4.5
            return (Weight * 0.33333) * malusMultiplicator;
        }

        // Questa funzione ritorna il tempo che deve passare dopo aver scaricato il colpo per caricarne un'altro
        public override TimeSpan GetDelay(Mobile m)
        {
            // Per ora ritorniamo il tempo di carica del colpo / 2.5 ma cmq almeno 0.85 secondi.
            double delay = GetSwingAnimationDelay(m) / 2.5;
            if (delay < 0.85)
                delay = 0.85;

            return TimeSpan.FromSeconds(delay);

        }

        public override SkillName GetUsedSkill(Mobile m, bool checkSkillAttrs)
        {
            Item item = m.FindItemOnLayer(Layer.OneHanded);

            if (item != null && item is ArmaBase)
                return ((ArmaBase)item).SkillUtilizzata;

            item = m.FindItemOnLayer(Layer.TwoHanded);

            if (item != null && item is ArmaBase)
                return ((ArmaBase)item).SkillUtilizzata;

            return SkillName.Wrestling;
        }

        public override double GetAttackSkillValue(Mobile attacker, Mobile defender)
        {
            return attacker.Skills[GetUsedSkill(attacker, true)].Value;
        }

        public override double GetDefendSkillValue(Mobile attacker, Mobile defender)
        {
            return defender.Skills[GetUsedSkill(defender, true)].Value;
        }

        // Overridden here. Non toccare.
        public override SkillName DefSkill { get { return DefSkillUtilizzata; } }
        public override WeaponType DefType { get { return DefTipo; } }
        public override WeaponAnimation DefAnimation { get { return DefAnimazione; } }
        public override int DefHitSound { get { return DefSuonoTargetColpito; } }
        public override int DefMissSound { get { return DefSuonoTargetMancato; } }
        public override int InitMinHits { get { return DefMinPuntiStruttura; } }
        public override int InitMaxHits { get { return DefMaxPuntiStruttura; } }
        public override int AosStrengthReq { get { return DefLivelloMinimo; } }
        public override int DefMaxRange { get { return DefMaxRangeScaricareColpo; } }
        public override Layer Layer { get { return DefLayer; } }
    

        // DNA funzioni, non toccare
        // TODO: this should not be virtual?
        public virtual int DannoMin { get { return DefDannoMinimo; } }
        public virtual int DannoMax { get { return DefDannoMassimo; } }
        public virtual SkillName SkillUtilizzata { get { return Skill; } set { Skill = value; } }
        public virtual WeaponAnimation Animazione { get { return Animation; } set { Animation = value; } }
        public virtual WeaponType Tipo { get { return Type; } set { Type = value; } }
        public virtual int SuonoTargetColpito { get { return HitSound; } set { HitSound = value; } }
        public virtual int SuonoTargetMancato { get { return MissSound; } set { MissSound = value; } }
        public virtual int LivelloMinimo { get { return AosStrengthReq; } }
        public override int MaxRangeCaricareColpo(Mobile attacker) { return DefMaxRangeCaricareColpo; }
        public virtual String Nome { get { return DefNome; } }
       

        // Questa funzione definisce cosa viene mostrato nel gump che si apre in automatico quando
        // passo con il mouse sull'arma.
        public override void GetProperties(ObjectPropertyList list)
        {
            // Nome arma. Per tutto il resto c'e il .osserva
            // TODO: in futuro si potrebbe fare automatico con il mouse, ovvero si mostrano info in base a chi sta osservando.
            list.Add(Nome);
        }

        public static String TipoToString(WeaponType tipo)
        {
            if (tipo == WeaponType.Punta)
                return "punta";
            else if (tipo == WeaponType.Taglio)
                return "taglio";
            else if (tipo == WeaponType.Contundenti)
                return "botta";
            else
                return "indefinita";
        }

        public static String SkillToString(SkillName skill)
        {
            if (skill == SkillName.Punta)
                return "punta";
            else if (skill == SkillName.Taglio)
                return "taglio";
            else if (skill == SkillName.Contundenti || skill == SkillName.Lottare)
                return "contundenti";
            else
                return "indefinita";
        }
        
        // Override non in uso e disponibili all'uso
        public override WeaponAbility PrimaryAbility { get { return WeaponAbility.ArmorIgnore; } }
        public override WeaponAbility SecondaryAbility { get { return WeaponAbility.ArmorIgnore; } }
        public override int AosMinDamage { get { return 14; } }
        public override int AosMaxDamage { get { return 15; } }
        public override int AosSpeed { get { return 33; } }
        public override float MlSpeed { get { return 3.25f; } }
        public override int OldStrengthReq { get { return 25; } }
        public override int OldMinDamage { get { return 5; } }
        public override int OldMaxDamage { get { return 29; } }
        public override int OldSpeed { get { return 45; } }         
	}
}
